﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace LearnCSharp.SqlMap.Extension
{
    public class TableInfoContainer
    {
        public static TableInfoContainer Instance { get; } = new TableInfoContainer();

        /// <summary>
        /// 存储集合
        /// </summary>
        private ConcurrentDictionary<Type, TableInfo> Tables = new ConcurrentDictionary<Type, TableInfo>();

        public TableInfo TryGet(Type target)
        {
            if (!Tables.TryGetValue(target, out var table))
            {
                table = Get(target);
            }

            return Tables.AddOrUpdate(target, table, updateFactory);
        }

        private Func<Type, TableInfo, TableInfo> updateFactory = (t, info) => { return info; };

        private TableInfo Get(Type targetType)
        {
            var fieldInfos = new List<FieldInfo>();

            var properties = targetType.GetProperties();
            var dataTaleAttribute = targetType.GetCustomAttribute<DataTableAttribute>();

            fieldInfos.AddRange(GetFields(properties).ToList());

            var tableInfo = new TableInfo(targetType.Name, fieldInfos)
            {
                TableName = dataTaleAttribute?.TableName,
                Type = targetType
            };

            return tableInfo;
        }

        /// <summary>
        /// 根据属性获取数据库字段信息
        /// </summary>
        /// <param name="properties"></param>
        /// <remarks>忽略没有配置特性的属性</remarks>
        /// <returns></returns>
        protected IEnumerable<FieldInfo> GetFields(PropertyInfo[] properties)
        {
            return properties.Select(a => GetField(a)).Where(x => x != null);
        }

        /// <summary>
        /// 获取每个属性的数据库信息
        /// </summary>
        /// <param name="propertyInfo"></param>
        /// <returns></returns>
        protected FieldInfo GetField(PropertyInfo propertyInfo)
        {
            var columnAttribute = propertyInfo.GetCustomAttribute<DataColumnAttribute>();

            if (columnAttribute == null)
            {
                return null;
            }

            if (string.IsNullOrEmpty(columnAttribute.DataType))
            {
                throw new ArgumentNullException($"{propertyInfo.PropertyType.FullName}没有指定数据库字段类型。");
            }

            var field = new FieldInfo(propertyInfo.Name, columnAttribute.DataType, columnAttribute.ColumnName)
            {
                PropertyInfo = propertyInfo,
            };

            return field;
        }

        public MemberInfo GetMember<TSource>(Expression<Func<TSource, dynamic>> fieldSelector)
        {
            MemberExpression memberExpression = fieldSelector.Body as MemberExpression;
            if (memberExpression == null)
            {
                UnaryExpression unaryExpression = fieldSelector as UnaryExpression;
                memberExpression = unaryExpression.Operand as MemberExpression;
            }

            return memberExpression.Member;
        }

        public FieldInfo GetColumn<TSource>(Expression<Func<TSource, dynamic>> fieldSelector)
        {
            var table = TryGet(typeof(TSource));

            var member = GetMember(fieldSelector);

            return table.GetFieldInfo(member.Name);
        }

        public string GetColumnName<TSource>(Expression<Func<TSource, dynamic>> fieldSelector)
        {
            return GetColumn<TSource>(fieldSelector).Name;
        }

        public string GetColumnDataType<TSource>(Expression<Func<TSource, dynamic>> fieldSelector)
        {
            var table = TryGet(typeof(TSource));

            var member = GetMember(fieldSelector);

            return GetColumn<TSource>(fieldSelector).DataType;
        }
    }
}
